package de.bensoft.jrail.client.com;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;

import javax.inject.Inject;
import javax.inject.Singleton;

import org.apache.log4j.Logger;

import de.bensoft.jrail.business.entity.Setting;
import de.bensoft.jrail.business.service.SettingsService;
import de.bensoft.jrail.client.com.interfaces.LI100;
import de.bensoft.jrail.client.ui.mainform.EmbeddedMessagingController;
import de.bensoft.jrail.client.ui.mainform.EmbeddedMessagingController.MessageType;

/**
 * This class is currently implement for using with LENZ LF101
 * @author Michael
 *
 */
@Singleton
public class SerialCommunicator {

	private static final Logger log = Logger.getLogger(SerialCommunicator.class);
	
	@Inject
	EmbeddedMessagingController messaging;
	
	@Inject
	SettingsService settingsService;
	
	@Inject
	private LI100 li100;
	public LI100 getLI100() { return li100; }
	
	private BooleanProperty connectedProperty;
	public BooleanProperty  connectedProperty() { return connectedProperty; }
	
	private Setting settingBaud, settingPort;
	
	private OutputStream out;
	
	public SerialCommunicator() {
		connectedProperty = new SimpleBooleanProperty();
		log.info("Communicator initialized!");
		
		
	}
	
	public void connect() {
		log.info("Trying to connect");
		
		settingBaud = settingsService.findSettingByIdentifier(SettingsService.SETTING_BAUD);
		settingPort = settingsService.findSettingByIdentifier(SettingsService.SETTING_PORT);
		
		if(messaging != null)
			messaging.addMessage(MessageType.INFO, "Communicator - Trying to connect ...");
		
		if(settingBaud.getLongValue() == null) {
			log.warn("Can't connect => Baudrate is not set");
			return;
		}
		
		if(settingPort.getStringValue() == null) {
			log.warn("Can't connect => Port is not set");
			return;
		}
		
		log.info("Trying to connect to " + settingPort.getStringValue());
		
		// RXTX Connect
		CommPortIdentifier cpi = null;
		try {
			cpi = CommPortIdentifier.getPortIdentifier(settingPort.getStringValue());
		} catch (NoSuchPortException e) {
			log.error(e);
			
			if(messaging != null)
				messaging.addMessage(MessageType.ERROR, settingPort.getStringValue() + " could not be found ..");
			
			e.printStackTrace();
			return;
		}
		
		if(cpi.isCurrentlyOwned()) {
			log.error(cpi.getName() + " is currently in use!!");
			messaging.addMessage(MessageType.ERROR, cpi.getName() + " is currently in use!!");
			return;
		} else {
			
			CommPort port = null;
			try {
				port = cpi.open(this.getClass().getName(), 2000);
			} catch (PortInUseException e) {
				log.error(cpi.getName() + " is currently in use!!");
				messaging.addMessage(MessageType.ERROR, cpi.getName() + " is currently in use!!");
				e.printStackTrace();
				return;
			}
			
			if(port instanceof SerialPort) {
				SerialPort sport = (SerialPort)port;
				try {
					
					
				sport.setSerialPortParams(settingBaud.getLongValue().intValue(),
											 SerialPort.DATABITS_8, 
											 SerialPort.STOPBITS_1,
											 SerialPort.PARITY_NONE);
				sport.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN
										| SerialPort.FLOWCONTROL_RTSCTS_OUT);
				sport.setRTS(true);
				
				InputStream is 	= sport.getInputStream();
                out 			= sport.getOutputStream();
                       		
                
                sport.addEventListener(new InputReader(is, this));
                sport.notifyOnDataAvailable(true);
                
                connectedProperty.set(true);
                
                
				} catch (Exception e) {
					log.error(cpi.getName() + " can't connect ... !");
					messaging.addMessage(MessageType.ERROR, cpi.getName() + " can't connect ... !");
					e.printStackTrace();
				} 
				
				
			} else {
				log.error(cpi.getName() + " only serialports are supported .... sorry !");
				messaging.addMessage(MessageType.ERROR, cpi.getName() + " only serialports are supported .... sorry !");
				return;
			}
			
		}	
		
		log.info("Successfull connected to " + cpi.getName());
		messaging.addMessage(MessageType.INFO, "Successfull connected to " + cpi.getName());
		
	}
	
	private static class InputReader implements SerialPortEventListener {

		private InputStream br;
		private SerialCommunicator sc;
		
		private byte[] buffer = new byte[1024];
		
		public InputReader(InputStream br, SerialCommunicator sc) {
			this.br = br;
			this.sc = sc;
		}
		
		public void serialEvent(SerialPortEvent arg0) {
			try {
				int size = br.read(buffer, 0, 1024);
				
				if(sc.getLI100() != null)
					sc.getLI100().incoming(buffer, 0, size);
				else
					log.warn("LI100 is not initialized!");
					
			} catch (IOException e) {
				e.printStackTrace();
			}			
		}
		
	}
	 
	 public void sendData(byte[] data) {
		 if(!connectedProperty.get())
			 return;
		 
		 try {
			out.write(data);
			out.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
		 
	 }
}
